package com.xyf.sys.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.*;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.profitsharing.ProfitsharingService;
import com.wechat.pay.java.service.profitsharing.model.QueryOrderAmountRequest;
import com.wechat.pay.java.service.profitsharing.model.QueryOrderAmountResponse;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.xyf.sys.config.CustomException;
import com.xyf.sys.config.MiniprogramConfig;
import com.xyf.sys.config.WxConfig;
import com.xyf.sys.domain.Order;
import com.xyf.sys.domain.OrderAddress;
import com.xyf.sys.domain.OrderProduct;
import com.xyf.sys.service.IWechatService;
import com.xyf.sys.util.WechatHttpUtil;
import com.xyf.sys.vo.OrderVo;
import com.xyf.sys.vo.PreOrderResponseVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

import static com.xyf.sys.util.WechatPayUtil.getRandomString;
import static com.xyf.sys.util.WechatPayUtil.sha256WithRsa;

/**
 * @Author R-H
 * @Date 2024/2/241755
 * @Description
 */
@Service
@Slf4j
public class WechatServiceImpl implements IWechatService {

    @Autowired
    private WxConfig wxConfig;
    @Autowired
    private JsapiService jsapiService;
    @Autowired
    private RefundService refundService;
    @Autowired
    private RefundService deliveryRefundService;
    @Autowired
    private RefundService laundryRefundService;
    @Autowired
    private ProfitsharingService profitsharingService;
    @Autowired
    private com.wechat.pay.java.service.payments.jsapi.JsapiService laundryJsapiService;
    @Autowired
    private com.wechat.pay.java.service.payments.jsapi.JsapiService deliveryJsapiService;
    @Autowired
    @Qualifier(value = "clientConfig")
    private MiniprogramConfig.MiniConfig clientConfig;


    @Override
    public String getAccessToken(String appid, String secret) {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
        String response = HttpUtil.get(url);
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (jsonObject.containsKey("errcode")) {
            log.error("wechat access token get err {}", jsonObject.toJSONString());
            throw new CustomException(jsonObject.getString("errmsg"));
        }
        String accessToken = jsonObject.getString("access_token");
        Integer expiresIn = jsonObject.getInteger("expires_in");
        return accessToken;
    }

    @Override
    public PreOrderResponseVo pay(String spAppid, String subMchid, Integer total, String openId, String outTradeNo, String description, Boolean profitSharing) {
        String prepayId = null;
        String privateKey = null;
        if (spAppid.equals(clientConfig.getAppid())){
            com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
            request.setAppid(spAppid);
            request.setMchid(subMchid);
            com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
            amount.setTotal(new BigDecimal(total).intValue());
            request.setAmount(amount);
            com.wechat.pay.java.service.payments.jsapi.model.Payer payer = new com.wechat.pay.java.service.payments.jsapi.model.Payer();
            payer.setOpenid(openId);
            request.setPayer(payer);
            if (profitSharing != null && profitSharing) {
                com.wechat.pay.java.service.payments.jsapi.model.SettleInfo settleInfo = new com.wechat.pay.java.service.payments.jsapi.model.SettleInfo();
                settleInfo.setProfitSharing(true);
                request.setSettleInfo(settleInfo);
            }
            request.setDescription(description);
            request.setOutTradeNo(outTradeNo);
            request.setNotifyUrl(wxConfig.getNotifyUrl());
            com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse prepay = null;
            if (wxConfig.getDeliveryMerchant().getMchid().equals(subMchid)) {
                prepay = deliveryJsapiService.prepay(request);
                privateKey = wxConfig.getDeliveryMerchant().getPrivateKey();
            } else {
                prepay = laundryJsapiService.prepay(request);
                privateKey = wxConfig.getLaundryMerchant().getPrivateKey();
            }
            log.info("wechat prepayResponse {}", prepay);
            prepayId = prepay.getPrepayId();
        } else {
            PrepayRequest request = new PrepayRequest();
            request.setSpAppid(spAppid);
            request.setSpMchid(wxConfig.getSpMchid());
            request.setSubMchid(subMchid);
            request.setSubAppid(spAppid);
            Amount amount = new Amount();
            amount.setTotal(new BigDecimal(total).intValue());
            request.setAmount(amount);
            if (profitSharing != null && profitSharing) {
                SettleInfo settleInfo = new SettleInfo();
                settleInfo.setProfitSharing(true);
                request.setSettleInfo(settleInfo);
            }
            request.setSupportFapiao(true);
            request.setDescription(description);
            request.setOutTradeNo(outTradeNo);
            request.setNotifyUrl(wxConfig.getNotifyUrl());
            Payer payer = new Payer();
            payer.setSpOpenid(openId);
            payer.setSubOpenid(openId);
            request.setPayer(payer);
            // 调用下单方法，得到应答
            PrepayResponse response = jsapiService.prepay(request);
            log.info("wechat pay response {}", response);
            prepayId = response.getPrepayId();
            privateKey = wxConfig.getPrivateKey();
        }
        PreOrderResponseVo preOrderResponseVo = new PreOrderResponseVo();
        String timeStamp = String.valueOf(System.currentTimeMillis());
        preOrderResponseVo.setTimeStamp(timeStamp);
        preOrderResponseVo.setAppId(spAppid);
        String nonce = getRandomString(32).toUpperCase();
        preOrderResponseVo.setNonceStr(nonce);
        preOrderResponseVo.setPackageStr("prepay_id=" + prepayId);
        preOrderResponseVo.setSignType("RSA");
        preOrderResponseVo.setPaySign(sha256WithRsa(spAppid, timeStamp, nonce, preOrderResponseVo.getPackageStr(), privateKey));
        return preOrderResponseVo;
    }

    @Override
    public Refund refund(String appid, String subMchid, String outTradeNo, String outRefundNo, BigDecimal refundAmount, BigDecimal totalAmount) {
        try {
            AmountReq req = new AmountReq();
            // 【退款金额】 退款金额，单位为分，只能为整数，不能超过原订单支付金额。
            req.setRefund(refundAmount.longValue());
            // 【原订单金额】 原支付交易的订单总金额，单位为分，只能为整数。
            req.setTotal(totalAmount.longValue());
            req.setCurrency("CNY");
            CreateRequest request = new CreateRequest();
            // 【商户退款单号】 商户系统内部的退款单号，商户系统内部唯一，只能是数字、大小写字母_-|*@ ，同一退款单号多次请求只退一笔。
            request.setOutTradeNo(outTradeNo);
            request.setOutRefundNo(outRefundNo);
            request.setAmount(req);
            request.setNotifyUrl(wxConfig.getRefundNotifyUrl());
            if (clientConfig.getAppid().equals(appid)){
                if (wxConfig.getDeliveryMerchant().getMchid().equals(subMchid)){
                    return deliveryRefundService.create(request);
                }
                if (wxConfig.getLaundryMerchant().getMchid().equals(subMchid)){
                    return laundryRefundService.create(request);
                }
            }
            request.setSubMchid(subMchid);
            return refundService.create(request);
        } catch (com.wechat.pay.java.core.exception.ServiceException e) {
            e.printStackTrace();
            log.error("wechat refund err {}", e);
            throw new CustomException(e.getErrorMessage());
        }
    }

    @Override
    public JSONArray textOCR(String appid, String secret, String imgUrl) throws UnsupportedEncodingException {
        String accessToken = this.getAccessToken(appid, secret);
        String url = "https://api.weixin.qq.com/cv/ocr/comm?access_token=" + accessToken + "&img_url=" + imgUrl;
        String result = WechatHttpUtil.post(url, new JSONObject());
        if (result == null){
            throw new CustomException("识别异常,请重试");
        }
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject.getInteger("errcode") != 0) {
            log.error("we chat text orc err {}", jsonObject.toJSONString());
            throw new CustomException("文字识别失败");
        }
        return jsonObject.getJSONArray("items");
    }

    @Override
    public JSONObject idCardOCR(String appid, String secret, String imgUrl) throws UnsupportedEncodingException {
        String accessToken = this.getAccessToken(appid, secret);
        String url = "https://api.weixin.qq.com/cv/ocr/idcard?access_token=" + accessToken + "&img_url=" + imgUrl;
        byte[] bytes = WechatHttpUtil.doWechatPost(url, new JSONObject());
        JSONObject jsonObject = JSONObject.parseObject(new String(bytes, "UTF-8"));
        log.info("img url {} data {}", imgUrl, jsonObject);
        if (jsonObject.getInteger("errcode") != 0) {
            log.error("we chat id card orc err {}", jsonObject.toJSONString());
            throw new CustomException("身份证识别失败");
        }
        return jsonObject;
    }

    @Override
    public JSONObject realName(String idcard, String name) {
        //云市场分配的密钥Id
        String secretId = "xxxx";
        //云市场分配的密钥Key
        String secretKey = "xxxx";
        String source = "market";
        return null;
    }

    @Override
    public JSONObject sendDeliveryOrderTemplateMessage(String appid, String secret, String template, String touser, OrderVo orderVo) {
        OrderProduct product = orderVo.getProductList().get(0);
        OrderAddress sender = product.getSendMan();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("touser", touser);
        jsonObject.put("template_id", template);
        JSONObject miniprogram = new JSONObject();
        miniprogram.put("appid", "wxc6ff511796ec714a");
        miniprogram.put("pagepath", "pages_regiment/regiment_orders_express");
        JSONObject data = new JSONObject();
        JSONObject thing8 = new JSONObject();
        thing8.put("value", sender.getName());
        data.put("thing8", thing8);
        JSONObject phoneNumber = new JSONObject();
        phoneNumber.put("value", sender.getMobile());
        data.put("phone_number9", phoneNumber);
        JSONObject thing7 = new JSONObject();
        thing7.put("value", sender.getProvince() + sender.getCity() + sender.getArea() + sender.getAddress());
        data.put("thing7", thing7);
        JSONObject time2 = new JSONObject();
        time2.put("value", DateUtil.format(orderVo.getOrderTime(), "MM-dd HH:mm"));
        data.put("time2", time2);
        jsonObject.put("data", data);
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + this.getAccessToken(appid, secret);
        String result = HttpUtil.post(url, jsonObject);
        log.info("open api res {}", result);
        if (result == null || StringUtils.isBlank(result)) {
            return null;
        }
        return JSONObject.parseObject(result);
    }


    @Override
    public JSONObject sendDeliveryOrderNotifyTemplateMessage(String appid, String secret, String template, String touser, Order order, String userName, String phone) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("touser", touser);
        jsonObject.put("template_id", template);
        JSONObject miniprogram = new JSONObject();
        miniprogram.put("appid", "wxc6ff511796ec714a");
        miniprogram.put("pagepath", "pages_regiment/regiment_orders_express");
        jsonObject.put("miniprogram", miniprogram);
        JSONObject data = new JSONObject();
        JSONObject orderCode = new JSONObject();
        orderCode.put("value", order.getOutTradeNo());
        data.put("character_string1", orderCode);
        JSONObject orderTime = new JSONObject();
        orderTime.put("value", DateUtil.format(order.getOrderTime(), "yyyy年MM月dd HH:mm"));
        data.put("time4", orderTime);
        JSONObject concat = new JSONObject();
        concat.put("value", userName);
        data.put("thing2", concat);
        JSONObject phoneNumber = new JSONObject();
        phoneNumber.put("value", phone);
        data.put("phone_number3", phoneNumber);
        jsonObject.put("data", data);
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + this.getAccessToken(appid, secret);
        String result = HttpUtil.post(url, jsonObject.toJSONString());
        log.info("open api res {}", result);
        if (result == null || StringUtils.isBlank(result)) {
            return null;
        }
        return JSONObject.parseObject(result);
    }

    @Override
    public JSONObject sendLaundryTemplateMessage(String appid, String secret, String template, String touser, OrderVo orderVo) {
        OrderAddress userAddress = orderVo.getUserAddress();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("touser", touser);
        jsonObject.put("template_id", template);
        JSONObject miniprogram = new JSONObject();
        miniprogram.put("appid", "wxc6ff511796ec714a");
        miniprogram.put("pagepath", "pages_regiment/regiment_orders_express");
        jsonObject.put("miniprogram", miniprogram);
        JSONObject data = new JSONObject();
        JSONObject character_string2 = new JSONObject();
        character_string2.put("value", DateUtil.format(orderVo.getOrderTime(), "MM-dd HH:mm"));
        data.put("character_string2", character_string2);
        JSONObject thing16 = new JSONObject();
        thing16.put("value", userAddress.getName());
        data.put("thing16", thing16);
        JSONObject phone_number7 = new JSONObject();
        phone_number7.put("value", userAddress.getMobile());
        data.put("phone_number7", phone_number7);
        JSONObject thing5 = new JSONObject();
        thing5.put("value", userAddress.getArea() + userAddress.getAddress());
        data.put("thing5", thing5);
        JSONObject time13 = new JSONObject();
        time13.put("value", DateUtil.format(orderVo.getOrderTime(), "MM-dd HH:mm"));
        data.put("time13", time13);
        jsonObject.put("data", data);
        log.info("body {}", jsonObject);
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + this.getAccessToken(appid, secret);
        byte[] bytes = WechatHttpUtil.doWechatPost(url, jsonObject);
        JSONObject result = null;
        try {
            result = JSONObject.parseObject(new String(bytes, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        log.info("open api res {}", result);
        return result;
    }

    @Override
    public JSONObject getUserInfo(String accessToken, String openid) {
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN";
        String response = HttpUtil.get(url);
        JSONObject jsonObject = JSONObject.parseObject(response);
        log.info("user info {}", jsonObject);
        if (jsonObject.containsKey("errcode")) {
            log.error("wechat get user info err {}", jsonObject.toJSONString());
            throw new CustomException(jsonObject.getString("errmsg"));
        }
        return jsonObject;
    }

    @Override
    public JSONObject replyFollowMessage(String message, String openid, String appid) {
        JSONObject body = new JSONObject();
        body.put("touser", openid);
        body.put("msgtype", "text");
        JSONObject text = new JSONObject();
        text.put("content", message);
        body.put("text", text);
        String url = "http://api.weixin.qq.com/cgi-bin/message/custom/send?from_appid=" + appid;
        byte[] bytes = WechatHttpUtil.doWechatPost(url, body);
        JSONObject result = null;
        try {
            result = JSONObject.parseObject(new String(bytes, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        log.info("reply res {}", result);
        return result;
    }

    @Override
    public void profitsharing(String subMchid, String appid, String transactionId, String outOrderNo, boolean unfreezeUnsplit) {
        //   profitsharingService.
    }

    @Override
    public QueryOrderAmountResponse queryProfitSharingAmounts(String transactionId) {
        QueryOrderAmountRequest queryOrderAmountRequest = new QueryOrderAmountRequest();
        queryOrderAmountRequest.setTransactionId(transactionId);
        log.info("queryProfitSharingAmounts transactionId id -> {} param - > {}", transactionId);
        QueryOrderAmountResponse response = profitsharingService.queryOrderAmount(queryOrderAmountRequest);
        log.info("query order amount response -> {}", JSONObject.toJSONString(response));
        return response;
    }

    @Override
    public JSONObject queryChannelsOrder(String orderId, String accessToken) {
        try {
            JSONObject orderParam = new JSONObject(4);
            orderParam.put("order_id", orderId);
            orderParam.put("encode_sensitive_info", false);
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/channels/ec/order/get?access_token=" + accessToken, orderParam);
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
            if (object.getInteger("errcode") == 0){
                return object.getJSONObject("order");
            } else {
                log.error("channel query order err {}", object);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String generateShortLink(String pageUrl, String pageTitle, String isPermanent, String accessToken) {
        try {
            JSONObject body = new JSONObject(4);
            body.put("page_url", pageUrl);
            body.put("page_title", pageTitle);
            body.put("is_permanent", StringUtils.isBlank(isPermanent) ? false : Boolean.parseBoolean(isPermanent));
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/wxa/genwxashortlink?access_token=" + accessToken, body);
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
            if (object.getInteger("errcode") == 0){
                return object.getString("link");
            } else {
                log.error("generate short link err {}", object);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public JSONArray getDeliveryList(String accessToken) {
        try {
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/get_delivery_list?access_token=" + accessToken, new JSONObject());
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
            if (object.getInteger("errcode") == 0){
                return object.getJSONArray("delivery_list");
            } else {
                log.error("getDeliveryList err {}", object);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new JSONArray();
    }

    @Override
    public void uploadShippingInfo(String transactionId, String trackingNo, String expressCompany, String itemDesc, String openid, String consignorContact, String receiverContact, Integer logisticsType, String accessToken) {
        JSONObject body = new JSONObject(4);
        JSONObject orderKey = new JSONObject();
        orderKey.put("order_number_type", 2);
        orderKey.put("transaction_id", transactionId);
        body.put("order_key", orderKey);
        // 物流模式，发货方式枚举值：1、实体物流配送采用快递公司进行实体物流配送形式
        // 2、同城配送
        // 3、虚拟商品，虚拟商品，例如话费充值，点卡等，无实体配送形式
        // 4、用户自提
        body.put("logistics_type", logisticsType);
        // 发货模式，发货模式枚举值：
        // 1、UNIFIED_DELIVERY（统一发货）
        // 2、SPLIT_DELIVERY（分拆发货） 示例值: UNIFIED_DELIVERY
        body.put("delivery_mode", 1);
        JSONArray shippingList = new JSONArray();
        JSONObject detail = new JSONObject();
        detail.put("tracking_no", trackingNo);
        detail.put("express_company", expressCompany.equalsIgnoreCase("YUNDA") ? "YD" : expressCompany.equalsIgnoreCase("EMS") ? "YZPY" : expressCompany);
        detail.put("item_desc", itemDesc);
        JSONObject contact = new JSONObject();
        contact.put("consignor_contact", DesensitizedUtil.mobilePhone(consignorContact));
        contact.put("receiver_contact", DesensitizedUtil.mobilePhone(receiverContact));
        detail.put("contact", contact);
        shippingList.add(detail);
        body.put("shipping_list", shippingList);
        body.put("upload_time", OffsetDateTime.now(ZoneOffset.ofHours(8)).toString());
        JSONObject payer = new JSONObject();
        payer.put("openid", openid);
        body.put("payer", payer);
        try {
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token=" + accessToken, body);
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
            if (object.getInteger("errcode") != 0){
                log.error("upload_shipping_info err {}", object);
                throw new CustomException(object.getString("errmsg"));
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void notifyConfirmReceive(String transactionId, String merchant_id, String merchant_trade_no, String accessToken) {
        JSONObject body = new JSONObject(4);
        body.put("transaction_id", transactionId);
        body.put("merchant_id", merchant_id);
        body.put("merchant_trade_no", merchant_trade_no);
        body.put("received_time", System.currentTimeMillis()/1000);
        try {
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive?access_token=" + accessToken, body);
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
            if (object.getInteger("errcode") != 0){
                log.error("notify_confirm_receive err {}", object);
                throw new CustomException(object.getString("errmsg"));
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public JSONObject setJumpPath(String path, String accessToken) {
        JSONObject body = new JSONObject();
        body.put("path", path);
        try {
            byte[] bytes = WechatHttpUtil.doWechatPost("https://api.weixin.qq.com/wxa/sec/order/set_msg_jump_path?access_token=" + accessToken, body);
            JSONObject object = JSONObject.parseObject(new String(bytes, "UTF-8"));
//            if (object.getInteger("errcode") != 0){
//                log.error("setJumpPath err {}", object);
//                throw new CustomException(object.getString("errmsg"));
//            }
            return object;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        throw new CustomException("设置失败");
    }
}
